package com.opener.common.http;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.opener.common.utils.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by baboy on 16/12/2016.
 */
public class HttpRequest {
    private Logger logger = LoggerFactory.getLogger(HttpRequest.class);
    public static String DATA_TYPE_JSON = "json";
    public static String DATA_TYPE_ENVELOPE_JSON = "envelope_json";
    public static String DATA_TYPE_STRING = "string";

    private String dataType = DATA_TYPE_STRING;
    private String charset = "UTF-8";
    private Map<String, String> headers;
    private byte[] postData;

    public String getDataType() {
        return dataType;
    }
    public void setDataType(String dataType) {
        this.dataType = dataType;
    }

    public Map getHeaders() {
        return headers;
    }

    public void setHeaders(Map headers) {
        this.headers = headers;
    }
    public void addHeader(String k,String v){
        if(headers == null){
            headers = new HashMap();
        }
        headers.put(k,v);
    }

    public byte[] getPostData() {
        return postData;
    }

    public void setPostData(byte[] postData) {
        this.postData = postData;
    }

    public HttpResponse query(String url) throws Exception{
        return query(url,null, HttpMethod.GET);
    }
    public HttpResponse query(String url, Map<String,String>params, HttpMethod method) throws Exception{
        HttpResponse response = new HttpResponse();
        String connUrl = url;
        method = method == null ? HttpMethod.GET : method;
        if( method == HttpMethod.GET && params != null){
            connUrl = formatUrl(url, params);
        }
        String decodeUrl = connUrl;
        byte[] postBody = postData;
        InputStream is = null;
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) new URL(decodeUrl).openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            if (headers != null){
                for (String k : headers.keySet()){
                    String v = headers.get(k);
                    conn.setRequestProperty(k, v);
                    if (k.equalsIgnoreCase("content-type") && "application/json".equalsIgnoreCase(v)){
                        if(method == HttpMethod.POST && params != null){
                            String c = new ObjectMapper().writeValueAsString(params);
                            postBody = c.getBytes(charset);
                        }
                    }
                }
            }
            switch (method) {
                case GET:
                    conn.setRequestMethod("GET");
                    conn.setUseCaches(true);
                    break;
                case POST:
                    conn.setUseCaches(false);
                    conn.setRequestMethod("POST");
                    if(postBody != null || params != null){
                        conn.setDoOutput(true);
                        OutputStream outputStream = conn.getOutputStream();
                        if (postBody != null){
                            outputStream.write(postBody);
                        }else if (params!=null) {
                            StringBuffer sb = new StringBuffer();
                            Iterator<String> iterator = params.keySet().iterator();
                            String sep = null;
                            while (iterator.hasNext()) {
                                String k = iterator.next();
                                String v = params.get(k);
                                if(sep!=null) {
                                    sb.append(sep);
                                }
                                if(v==null){
                                    v = "";
                                }
                                sb.append(k+"="+URLEncoder.encode(v,"UTF-8"));
                                sep = "&";
                            }
                            String paramString = sb.toString();
                            outputStream.write(paramString.getBytes("UTF-8"));
                        }
                        outputStream.flush();
                        outputStream.close();
                    }

                    break;
            }
            is = conn.getInputStream();
            /****************/
            int contentLength = conn.getContentLength();
            int PER_BUFFER_SIZE = 100*1024;
            if(contentLength<=0) {
                contentLength = PER_BUFFER_SIZE;
            }
            byte[] data = null;
            if (dataType.equals(DATA_TYPE_STRING) ||
                    dataType.equals(DATA_TYPE_JSON) ||
                    dataType == DATA_TYPE_ENVELOPE_JSON) {
                data = new byte[contentLength];
            }

            byte[] buffer = new byte[81920];
            int len = 0, dataLength = 0;
            while( (len = is.read(buffer)) >= 0){
                if( (dataLength + len) > contentLength ){
                    contentLength += PER_BUFFER_SIZE > len ? PER_BUFFER_SIZE : len;
                    byte[] tmp = new byte[contentLength];
                    System.arraycopy(data, 0, tmp, 0, dataLength);
                    data = tmp;
                }
                if(data !=null){
                    System.arraycopy(buffer, 0, data, dataLength, len);
                }
                dataLength += len;
            }
            if (data != null){
                String s = new String(data,0, dataLength,charset);
                if (dataType.equals(DATA_TYPE_ENVELOPE_JSON)){
                    Map ret = (Map) JsonUtils.parse(s);
                    response.setStatus(Integer.parseInt(ret.get("status").toString()));
                    response.setData(ret.get("data"));
                }else if (dataType.equals(DATA_TYPE_JSON)){
                    Object ret = JsonUtils.parse(s);
                    response.setData(ret);
                }else{
                    response.setData(s);
                }
            }
            return response;
        } catch (Exception e) {
            response.setStatus(StatusCode.ERROR);
            response.setMsg(e.getMessage());
            logger.debug("HTTP REQUEST:"+decodeUrl);
            throw e;

        } finally {
            try {
                is.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    public static String formatUrl(String url, Map<String, String> param, boolean append) {
        try {
            if (param == null){
                return url;
            }
            String newUrl = url;
            for (String k : param.keySet()){
                String v = param.get(k);
                v = v == null ?"":URLEncoder.encode(v, "UTF-8");
                String ph = "{"+k+"}";
                boolean isInclude = newUrl.indexOf(ph) >=0;
                if ( isInclude ){
                    newUrl = newUrl.replace(ph, v);
                }
                if(!isInclude && append){
                    if (newUrl.indexOf("?") < 0){
                        newUrl = newUrl + "?";
                    }
                    if (newUrl.indexOf("?") < (newUrl.length() - 1)){
                        newUrl = newUrl + "&";
                    }
                    newUrl = newUrl + k + "=" + v;
                }
            }
            return newUrl;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public static String formatUrl(String url, Map<String, String> param) {
        return formatUrl(url, param, true);
    }

    public static String encodeUrl(String url) {
        StringBuffer newUrl = null;
        try {
            URL aURL = new URL(url);
            newUrl = new StringBuffer(aURL.getProtocol());
            newUrl.append("://");
            newUrl.append(aURL.getHost());
            if (aURL.getPort() > 0) {
                newUrl.append(":").append(aURL.getPort());
            }
            String arr[] = aURL.getPath().split("/");
            int n = arr.length;
            for (int i = 0; i < n; i++) {
                String s = arr[i];
                if (!s.equals("")) {
                    newUrl.append("/").append(URLEncoder.encode(s, "UTF-8"));
                }
            }
            if (aURL.getQuery() != null) {
                newUrl.append("?");
                String a[] = aURL.getQuery().split("&");
                int n1 = a.length;
                String sep = null;
                for (int i = 0; i < n1; i++) {
                    String s = a[i];
                    String a2[] = s.split("=");
                    int n2 = a2.length;
                    if (n2 < 1) {
                        continue;
                    }
                    String k = a2[0];
                    String v = n2 > 1 ? a2[1] : null;
                    sep = sep == null ? "" : "&";
                    newUrl.append(sep).append(k);
                    if (v != null) {
                        newUrl.append("=").append(URLEncoder.encode(v, "UTF-8"));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return newUrl.toString();
    }
}
